package tokens

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	jwt "github.com/golang-jwt/jwt/v4"
	jsoniter "github.com/json-iterator/go"
	"io/ioutil"
	"net/http"
	"net/url"
	"sync"
	"time"
)

// apple server api jwt private key
const (
	APPLE_SERVER_CERT    = "./config/SubscriptionKey_65CK3LSXM7.p8"
	CERT_KEY_ID          = "65CK3LSXM7"
	APPLE_PROJECT_ISSUER = "69a6de82-0563-47e3-e053-5b8c7c11a4d1"
	//APPLE_PROJECT_BID    = "com.td.watcherofrealms"
	JWT_AUD             = "appstoreconnect-v1"
	JWT_TOKEN_LIVE_TIME = 60
	GOOGLE_CREDENTIAL   = "{\n  \"type\": \"service_account\",\n  \"project_id\": \"api-8532907581042995001-149771\",\n  \"private_key_id\": \"d84528d67fe78e8c82ceb386b607b7f3796472a8\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCi3zOimTBfoDKv\\nFkxpkWcSQzFVZoStBcnrBBjdBTSmTrWeBmobU5m0FQhD/2jROJW/wS07QXcTcp0y\\nFlG7fOpFWYQP+TYZtRGMSOtYXAc3LsRwOteHpqzWRgoM/kkwr+00wwM/8ahByiRX\\nNdZEyHkL0IZgepDUDBPcPblHsMfinzoX0/o2fzomx25BXR+tHikZbN4OkuoyDeXa\\noA/e75fYUpC7BokNGAPVjeJCxsEDs/kcoXOj7OYUKMzChUdsc03jSdbAQvvEea+K\\n+b94huoc1hRGBHBXWY3Zm34n/WZ7X3BjItT5wzbpni1N4n2paHOYRlyJGV9esgJl\\nZ2uthrvPAgMBAAECggEACpJTRIFjTD8BMIUm1jlWXS9C/f3mrGEHpKYCQ8z+iFxB\\nL0Mk/aYVABqCDAEsLrGuDUwUIVhT9XgHL6+W2YUM+GMMOTSIqELXoz6TwWCjz+yi\\nBSjZ7Cz2OKhDfGlq8GqS382tTH1lLvQDHNgFPYyySAEzylevm9vhfsumLDrowShQ\\nU/22QxCziGGLVEuu2jbxQMweHcMew32lxTsxVLC4pos+5uI6dtoeELwb4k9YWSvJ\\nE4vrzJ/uPME8EwGXkJ1wbXw1WOnU2pZYuTspVopQGNw9+EGhPJpe10P8OO++SU0v\\nak2qYM01YWRvg6ssmfj42LckEt3QDxlwNjBFfaVhAQKBgQDiSUIuJLWIpVhBByDa\\n1mi4kok5I9yhRaKtUMy+midQomOKvTR61bGvyPVhTy4qKuDWHfjORPJezz5rSW+p\\nZ00tdhFQP5pDkpRE2Nfoqm7Xlj1DKsULhg3ozkoVNqMuMhppDBiaVH/jkVmKJdU0\\n4faAnjUIxd9ozvBFRthnep8HAQKBgQC4QjwqFewpQsf9vohZTnDyCsYPdutpwW9T\\n2QkyHzx8vFquMxzuC3kCbCw4jHO6l/EKs6dAQ59fW6fXQ0hbCUVl7iPxjBe8xgeZ\\n181pNpv9rDMfAscuwtDEuzpAQU4R6HhevlsZ+CCFG8HUWSDRHD7qntWCivlab+5k\\n93O5OnISzwKBgQC8Gufv0cYyLDib/d/66ocs/2sg28XZLjjX2DYLL7wE1KjouPW6\\nMo+9r9EDLj4TAYfblhiw5zGPTLn3l5lB+kR2yRIFYonLKDsmrBd3Ks5vWk3c2t3o\\nAWzonSuKTNwDV9UKg+qjyCBzvOzw4RhGGJzAOEOf4lUgG/4xqH3WL2GpAQKBgCbw\\niHp0a5ZEsLMlQvdspl3gfP7jqNiOspJLua/H/iZ2P8u0rZZ5AjfK2IWS2RpD5gLW\\n+K+SGuoyGoRZib8DdJEYBKyVaKbSSuTuQFDN46V2cM9K7QEq1qfc0XCm3HLYaCfN\\nnr8cOvMmbI4Nz+uhRoKa0pBmAlBke0MHMIOioFztAoGAUzTtN7ctBGMFTWT+ffHJ\\ntQk9zM3wVzrmc8lsYELhjUQg1fKd65dztwk5iBnE1TOJ5U1sgL00FTvILOIWoEAz\\nny3vlCG9QGBrFgwFGFryMjsgafd6Qm2kSSlfkh+AZzK6TGBi40KbJiJ3EHehWb0w\\n32pkB2HJgoZLNTNtzJsyNVI=\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"zgame-internal-app-sharing@api-8532907581042995001-149771.iam.gserviceaccount.com\",\n  \"client_id\": \"103504013798095267531\",\n  \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n  \"token_uri\": \"https://oauth2.googleapis.com/token\",\n  \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n  \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/zgame-internal-app-sharing%40api-8532907581042995001-149771.iam.gserviceaccount.com\"\n}\n"
	PACKAGE_NAME        = "com.td.watcherofrealms"
)

type Credential struct {
	Type                    string `json:"type"`
	ProjectID               string `json:"project_id"`
	PrivateKeyID            string `json:"private_key_id"`
	PrivateKey              string `json:"private_key"`
	ClientEmail             string `json:"client_email"`
	ClientID                string `json:"client_id"`
	AuthURI                 string `json:"auth_uri"`
	TokenURI                string `json:"token_uri"`
	AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"`
	ClientX509CertURL       string `json:"client_x509_cert_url"`
}

var (
	cacheToken sync.Map
	credential Credential
)

func init() {
	err := jsoniter.UnmarshalFromString(GOOGLE_CREDENTIAL, &credential)
	if err != nil {
		panic(fmt.Errorf("GP ServiceAccount凭证解析错误 %v\n", err))
		return
	}
}

func refreshAppleServerApiToken() (token *CacheToken, err error) {
	key, err := ioutil.ReadFile(APPLE_SERVER_CERT)
	if err != nil {
		return
	}
	//block, _ := pem.Decode([]byte(JWT_SECRET))
	//ecdsaKey, err := x509.ParseECPrivateKey(block.Bytes)
	ecdsaKey, err := jwt.ParseECPrivateKeyFromPEM(key)
	if err != nil {
		return
	}
	ExpiresAt := time.Now().Add(time.Minute * JWT_TOKEN_LIVE_TIME)
	uAccessToken := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{
		"iss": APPLE_PROJECT_ISSUER,
		"iat": time.Now().Unix(),
		"exp": ExpiresAt.Unix(),
		"aud": JWT_AUD,
		"bid": PACKAGE_NAME,
	})
	uAccessToken.Header["kid"] = CERT_KEY_ID
	accessToken, err := uAccessToken.SignedString(ecdsaKey)
	if err != nil {
		return
	}

	token = &CacheToken{
		Token:     accessToken,
		ExpiresAt: ExpiresAt,
	}
	return token, nil
}

func refreshGoogleServerApiToken() (*CacheToken, error) {
	//fmt.Printf("refresh token for %s\n", credential.ClientEmail)
	p, _ := pem.Decode([]byte(credential.PrivateKey))

	privKey, err := x509.ParsePKCS8PrivateKey(p.Bytes)
	if err != nil {
		return nil, fmt.Errorf("GP ServiceAccount私钥x509解析错误 %s", err)
	}

	jwtHeader := base64.StdEncoding.EncodeToString([]byte(`{"alg":"RS256","typ":"JWT"}`))
	var claimSet struct {
		Iss   string `json:"iss"`
		Scope string `json:"scope"`
		Aud   string `json:"aud"`
		Exp   uint   `json:"exp"`
		Iat   uint   `json:"iat"`
	}
	claimSet.Iss = credential.ClientEmail
	claimSet.Scope = "https://www.googleapis.com/auth/androidpublisher"
	claimSet.Aud = credential.TokenURI
	var now = uint(time.Now().Unix())
	claimSet.Exp = now + 3600
	claimSet.Iat = now
	jwtClaimSet, _ := jsoniter.MarshalToString(claimSet)
	jwtClaimSet = base64.StdEncoding.EncodeToString([]byte(jwtClaimSet))

	hashed := sha256.Sum256([]byte(jwtHeader + "." + jwtClaimSet))
	signature, err := rsa.SignPKCS1v15(rand.Reader, privKey.(*rsa.PrivateKey), crypto.SHA256, hashed[:])
	if err != nil {
		return nil, err
	}
	assertion := jwtHeader + "." + jwtClaimSet + "." + base64.StdEncoding.EncodeToString(signature)
	v := url.Values{}
	v.Add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
	v.Add("assertion", assertion)
	resp, err := http.Post(credential.TokenURI, "application/x-www-form-urlencoded", bytes.NewBufferString(v.Encode()))
	if err != nil {
		return nil, err
	}

	//fmt.Printf("request %s\n", v.Encode())
	defer resp.Body.Close()
	respData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	var respJson struct {
		AccessToken string `json:"access_token"`
		Scope       string `json:"scope"`
		TokenType   string `json:"token_type"`
		ExpiresIn   int    `json:"expires_in"`
	}
	err = jsoniter.Unmarshal(respData, &respJson)
	if err != nil {
		return nil, err
	}

	//fmt.Printf("response %s\n", respJson)

	return &CacheToken{
		Token:     respJson.AccessToken,
		ExpiresAt: time.Now().Add(time.Duration(respJson.ExpiresIn) * time.Second),
	}, nil
}

func GetCacheToken(key string) (sToken string, err error) {
	token, ok := cacheToken.Load(key)
	if ok {
		token := token.(*CacheToken)
		if time.Now().Add(time.Second * 60).Before(token.ExpiresAt) {
			return token.Token, nil
		}
	}
	var newToken *CacheToken
	switch key {
	case "gp":
		newToken, err = refreshGoogleServerApiToken()
	case "ios":
		newToken, err = refreshAppleServerApiToken()
	default:
		return "", fmt.Errorf("unsport key:%s platform", key)
	}

	if err != nil || newToken == nil {
		return "", err
	}
	cacheToken.Store(key, newToken)
	return newToken.Token, nil
}
